--- /dev/null
+No,Latitude,Longitude,Name,GCID\r
+1,1.000000,2.000000,"WPT001",1\r
+2,1.000000,2.000000,"WPT002",1\r
+3,1.000000,2.000000,"WPT003",1\r
+4,1.000000,2.000000,"WPT004",1\r
+5,1.000000,2.000000,"WPT005",16\r
+6,1.000000,2.000000,"WPT006",256\r
+7,1.000000,2.000000,"WPT007",4096\r
+8,1.000000,2.000000,"WPT008",20480\r
+9,1.000000,2.000000,"WPT009",65535\r
+10,1.000000,2.000000,"WPT010",65536\r
+11,1.000000,2.000000,"WPT011",512401\r
+12,1.000000,2.000000,"WPT012",14365216\r
+13,1.000000,2.000000,"WPT013",27294510\r
+14,1.000000,2.000000,"WPT014",27294515\r
+15,1.000000,2.000000,"WPT015",28218030\r
+16,1.000000,2.000000,"WPT016",887092560\r
+17,1.000000,2.000000,"WPT017",27512202990\r
+18,1.000000,2.000000,"WPT018",852890626320\r
+19,1.000000,2.000000,"WPT019",26439621749550\r
+20,1.000000,2.000000,"WPT020",819628286569680\r
+21,1.000000,2.000000,"WPT021",25408476895993710\r
+22,1.000000,2.000000,"WPT022",787662783788138640\r
+23,1.000000,2.000000,"WPT023",\r
+24,1.000000,2.000000,"WPT024",\r
+25,1.000000,2.000000,"WPT025",\r
+26,1.000000,2.000000,"WPT026",\r
+27,1.000000,2.000000,"WPT027",\r
+28,1.000000,2.000000,"WPT028",\r
+29,1.000000,2.000000,"WPT029",\r
+30,1.000000,2.000000,"WPT030",\r
+31,1.000000,2.000000,"WPT031",\r
+32,1.000000,2.000000,"WPT032",\r
+33,1.000000,2.000000,"WPT033",\r
+34,1.000000,2.000000,"WPT034",\r
+35,1.000000,2.000000,"WPT035",\r
+36,1.000000,2.000000,"WPT036",\r
+37,1.000000,2.000000,"WPT037",\r
+38,1.000000,2.000000,"WPT038",\r
+39,1.000000,2.000000,"WPT039",\r
+40,1.000000,2.000000,"WPT040",\r
+41,1.000000,2.000000,"WPT041",\r
+42,1.000000,2.000000,"WPT042",\r
+43,1.000000,2.000000,"WPT043",\r
+44,1.000000,2.000000,"WPT044",\r
+45,1.000000,2.000000,"WPT045",\r
+46,1.000000,2.000000,"WPT046",\r
+47,1.000000,2.000000,"WPT047",\r
+48,1.000000,2.000000,"WPT048",\r
+49,1.000000,2.000000,"WPT049",\r
+50,1.000000,2.000000,"WPT050",\r
+51,1.000000,2.000000,"WPT051",\r
+52,1.000000,2.000000,"WPT052",\r
+53,1.000000,2.000000,"WPT053",\r
+54,1.000000,2.000000,"WPT054",12345\r
+55,1.000000,2.000000,"WPT055",123456\r
+56,1.000000,2.000000,"WPT056",787662783788138640\r
/* helpers */
-// There is no test coverage of this and it's been wrong for years and
-// nobody has noticed...
-static int
-unicsv_parse_gc_id(const QString& str)
+// Parse GC-Code / geo cache reference code into int64 (GC-ID)
+// (see also https://api.groundspeak.com/documentation#referencecodes)
+static long long
+unicsv_parse_gc_code(const QString& str)
{
- int res = 0;
- const QString kBase35 = "0123456789ABCDEFGHJKMNPQRTVWXYZ"; // ILOSU are omitted.
- if (str.startsWith("GC")) {
- int base35 = str.size() > 6; // above GCFFFF?
- QString s = str.mid(2);
- while (!s.isEmpty()) {
- res = res * 16 + kBase35.indexOf(s[0]);
- s = str.mid(1);
+ if (! str.startsWith("GC")) {
+ return 0;
+ }
+
+ // Remove "GC" prefix
+ QString s = str.mid(2);
+ // Replacements according to groundspeak api documentation
+ s.replace('S', '5');
+ s.replace('O', '0');
+ // Remove leading zeros. some online converters do that as well.
+ while (s.startsWith('0')) {
+ s.remove(0, 1);
+ }
+
+ // We have these cases:
+ // * 1-3 digits => base 16
+ // * 4 digits, first one is 0-F => base 16
+ // * 4 digits, first one G-Z => base 31
+ // * 5-12 digits => base 31
+ // * 13- digits => exceeds int64_t
+ //
+ int base;
+ const QString kBase31 = "0123456789ABCDEFGHJKMNPQRTVWXYZ"; // ILOSU are omitted.
+ if (s.size() >= 1 && s.size() <= 3) {
+ base = 16;
+ } else if (s.size() == 4) {
+ if (kBase31.indexOf(s[0]) < 16) {
+ base = 16;
+ } else {
+ base = 31;
}
- if (base35) {
- res -= 411120;
+ } else if (s.size() >= 5 && s.size() <= 12) {
+ base = 31;
+ } else {
+ return 0;
+ }
+
+ long long res = 0;
+ for (auto c : qAsConst(s)) {
+ int val = kBase31.indexOf(c);
+ if (val < 0 || (base == 16 && val > 15)) {
+ return 0;
}
+ res = res * base + val;
+ }
+ if (base == 31) {
+ res -= 411120;
+ }
+ if (res < 0) {
+ res = 0;
}
return res;
}
switch (unicsv_fields_tab[column]) {
case fld_gc_id:
- gc_data->id = value.toInt();
- if (gc_data->id == 0) {
- gc_data->id = unicsv_parse_gc_id(value);
+ // First try to decode as numeric GC-ID (e.g. "575006").
+ // If that doesn't succedd, try to decode as GC-Code
+ // (e.g. "GC1234G").
+ bool ok;
+ gc_data->id = value.toLongLong(&ok, 10);
+ if (!ok) {
+ gc_data->id = unicsv_parse_gc_code(value);
}
break;
case fld_gc_type: